home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 126-150 / disk_149 / less / src / prim.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  13KB  |  746 lines

  1. /*
  2.  * Primitives for displaying the file on the screen.
  3.  */
  4.  
  5. #include "less.h"
  6. #include "position.h"
  7.  
  8. public int hit_eof;    /* Keeps track of how many times we hit end of file */
  9.  
  10. extern int quiet;
  11. extern int top_search;
  12. extern int top_scroll;
  13. extern int back_scroll;
  14. extern int sc_width, sc_height;
  15. extern int sigs;
  16. extern int quit_at_eof;
  17. extern int ac;
  18. extern char *line;
  19. extern char *first_cmd;
  20.  
  21. /*
  22.  * Sound the bell to indicate he is trying to move past end of file.
  23.  */
  24.     static void
  25. eof_bell()
  26. {
  27.     if (quiet == NOT_QUIET)
  28.         bell();
  29.     else
  30.         vbell();
  31. }
  32.  
  33. /*
  34.  * Check to see if the end of file is currently "displayed".
  35.  */
  36.     static void
  37. eof_check()
  38. {
  39.     POSITION pos;
  40.  
  41.     /*
  42.      * If the bottom line is empty, we are at EOF.
  43.      * If the bottom line ends at the file length,
  44.      * we must be just at EOF.
  45.      */
  46.     pos = position(BOTTOM_PLUS_ONE);
  47.     if (pos == NULL_POSITION || pos == ch_length())
  48.         hit_eof++;
  49. }
  50.  
  51. /*
  52.  * Display n lines, scrolling forward, 
  53.  * starting at position pos in the input file.
  54.  * "force" means display the n lines even if we hit end of file.
  55.  * "only_last" means display only the last screenful if n > screen size.
  56.  */
  57.     static void
  58. forw(n, pos, force, only_last)
  59.     register int n;
  60.     POSITION pos;
  61.     int force;
  62.     int only_last;
  63. {
  64.     int eof = 0;
  65.     int nlines = 0;
  66.     int do_repaint;
  67.     static int first_time = 1;
  68.  
  69.     /*
  70.      * do_repaint tells us not to display anything till the end, 
  71.      * then just repaint the entire screen.
  72.      */
  73.     do_repaint = (only_last && n > sc_height-1);
  74.  
  75.     if (!do_repaint)
  76.     {
  77.         if (top_scroll && n >= sc_height - 1)
  78.         {
  79.             /*
  80.              * Start a new screen.
  81.              * {{ This is not really desirable if we happen
  82.              *    to hit eof in the middle of this screen,
  83.              *    but we don't yet know if that will happen. }}
  84.              */
  85.             if (top_scroll == 2)
  86.                 clear();
  87.             home();
  88.             force = 1;
  89.         } else
  90.         {
  91.             lower_left();
  92.             clear_eol();
  93.         }
  94.  
  95.         if (pos != position(BOTTOM_PLUS_ONE))
  96.         {
  97.             /*
  98.              * This is not contiguous with what is
  99.              * currently displayed.  Clear the screen image 
  100.              * (position table) and start a new screen.
  101.              */
  102.             pos_clear();
  103.             add_forw_pos(pos);
  104.             force = 1;
  105.             if (top_scroll)
  106.             {
  107.                 if (top_scroll == 2)
  108.                     clear();
  109.                 home();
  110.             } else if (!first_time)
  111.             {
  112.                 putstr("...skipping...\n");
  113.             }
  114.         }
  115.     }
  116.  
  117.     while (--n >= 0)
  118.     {
  119.         /*
  120.          * Read the next line of input.
  121.          */
  122.         pos = forw_line(pos);
  123.         if (pos == NULL_POSITION)
  124.         {
  125.             /*
  126.              * End of file: stop here unless the top line 
  127.              * is still empty, or "force" is true.
  128.              */
  129.             eof = 1;
  130.             if (!force && position(TOP) != NULL_POSITION)
  131.                 break;
  132.             line = NULL;
  133.         }
  134.         /*
  135.          * Add the position of the next line to the position table.
  136.          * Display the current line on the screen.
  137.          */
  138.         add_forw_pos(pos);
  139.         nlines++;
  140.         if (do_repaint || 
  141.             (first_time && line == NULL && !top_scroll))
  142.             continue;
  143.         if (top_scroll == 1)
  144.             clear_eol();
  145.         put_line();
  146.     }
  147.  
  148.     if (eof)
  149.         hit_eof++;
  150.     else
  151.         eof_check();
  152.     if (nlines == 0)
  153.         eof_bell();
  154.     else if (do_repaint)
  155.         repaint();
  156.     if (first_time && hit_eof && quit_at_eof && ac <= 1)
  157.         quit();
  158.     first_time = 0;
  159. }
  160.  
  161. /*
  162.  * Display n lines, scrolling backward.
  163.  */
  164.     static void
  165. back(n, pos, force, only_last)
  166.     register int n;
  167.     POSITION pos;
  168.     int force;
  169.     int only_last;
  170. {
  171.     int nlines = 0;
  172.     int do_repaint;
  173.  
  174.     do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1));
  175.     hit_eof = 0;
  176.     while (--n >= 0)
  177.     {
  178.         /*
  179.          * Get the previous line of input.
  180.          */
  181.         pos = back_line(pos);
  182.         if (pos == NULL_POSITION)
  183.         {
  184.             /*
  185.              * Beginning of file: stop here unless "force" is true.
  186.              */
  187.             if (!force)
  188.                 break;
  189.             line = NULL;
  190.         }
  191.         /*
  192.          * Add the position of the previous line to the position table.
  193.          * Display the line on the screen.
  194.          */
  195.         add_back_pos(pos);
  196.         nlines++;
  197.         if (!do_repaint)
  198.         {
  199.             home();
  200.             add_line();
  201.             put_line();
  202.         }
  203.     }
  204.  
  205.     eof_check();
  206.     if (nlines == 0)
  207.         eof_bell();
  208.     else if (do_repaint)
  209.         repaint();
  210. }
  211.  
  212. /*
  213.  * Display n more lines, forward.
  214.  * Start just after the line currently displayed at the bottom of the screen.
  215.  */
  216.     public void
  217. forward(n, only_last)
  218.     int n;
  219.     int only_last;
  220. {
  221.     POSITION pos;
  222.  
  223.     pos = position(BOTTOM_PLUS_ONE);
  224.     if (pos == NULL_POSITION)
  225.     {
  226.         eof_bell();
  227.         hit_eof++;
  228.         return;
  229.     }
  230.     forw(n, pos, 0, only_last);
  231. }
  232.  
  233. /*
  234.  * Display n more lines, backward.
  235.  * Start just before the line currently displayed at the top of the screen.
  236.  */
  237.     public void
  238. backward(n, only_last)
  239.     int n;
  240.     int only_last;
  241. {
  242.     POSITION pos;
  243.  
  244.     pos = position(TOP);
  245.     if (pos == NULL_POSITION)
  246.     {
  247.         /* 
  248.          * This will almost never happen,
  249.          * because the top line is almost never empty. 
  250.          */
  251.         eof_bell();
  252.         return;   
  253.     }
  254.     back(n, pos, 0, only_last);
  255. }
  256.  
  257. /*
  258.  * Repaint the screen, starting from a specified position.
  259.  */
  260.     static void
  261. prepaint(pos)    
  262.     POSITION pos;
  263. {
  264.     hit_eof = 0;
  265.     forw(sc_height-1, pos, 1, 0);
  266. }
  267.  
  268. /*
  269.  * Repaint the screen.
  270.  */
  271.     public void
  272. repaint()
  273. {
  274.     /*
  275.      * Start at the line currently at the top of the screen
  276.      * and redisplay the screen.
  277.      */
  278.     prepaint(position(TOP));
  279. }
  280.  
  281. /*
  282.  * Jump to the end of the file.
  283.  * It is more convenient to paint the screen backward,
  284.  * from the end of the file toward the beginning.
  285.  */
  286.     public void
  287. jump_forw()
  288. {
  289.     POSITION pos;
  290.  
  291.     if (ch_end_seek())
  292.     {
  293.         error("Cannot seek to end of file");
  294.         return;
  295.     }
  296.     lastmark();
  297.     pos = ch_tell();
  298.     clear();
  299.     pos_clear();
  300.     add_back_pos(pos);
  301.     back(sc_height - 1, pos, 0, 0);
  302. }
  303.  
  304. /*
  305.  * Jump to line n in the file.
  306.  */
  307.     public void
  308. jump_back(n)
  309.     register int n;
  310. {
  311.     register int c;
  312.     int nlines;
  313.  
  314.     /*
  315.      * This is done the slow way, by starting at the beginning
  316.      * of the file and counting newlines.
  317.      */
  318.     if (ch_seek((POSITION)0))
  319.     {
  320.         /* 
  321.          * Probably a pipe with beginning of file no longer buffered. 
  322.          * If he wants to go to line 1, we do the best we can, 
  323.          * by going to the first line which is still buffered.
  324.          */
  325.         if (n <= 1 && ch_beg_seek() == 0)
  326.             jump_loc(ch_tell());
  327.         error("Cannot get to beginning of file");
  328.         return;
  329.     }
  330.  
  331.     /*
  332.      * Start counting lines.
  333.      */
  334.     for (nlines = 1;  nlines < n;  nlines++)
  335.     {
  336.         while ((c = ch_forw_get()) != '\n')
  337.             if (c == EOF)
  338.             {
  339.                 char message[40];
  340.                 sprintf(message, "File has only %d lines", 
  341.                     nlines-1);
  342.                 error(message);
  343.                 return;
  344.             }
  345.     }
  346.  
  347.     jump_loc(ch_tell());
  348. }
  349.  
  350. /*
  351.  * Jump to a specified percentage into the file.
  352.  * This is a poor compensation for not being able to
  353.  * quickly jump to a specific line number.
  354.  */
  355.     public void
  356. jump_percent(percent)
  357.     int percent;
  358. {
  359.     POSITION pos, len;
  360.     register int c;
  361.  
  362.     /*
  363.      * Determine the position in the file
  364.      * (the specified percentage of the file's length).
  365.      */
  366.     if ((len = ch_length()) == NULL_POSITION)
  367.     {
  368.         error("Don't know length of file");
  369.         return;
  370.     }
  371.     pos = (percent * len) / 100;
  372.  
  373.     /*
  374.      * Back up to the beginning of the line.
  375.      */
  376.     if (ch_seek(pos) == 0)
  377.     {
  378.         while ((c = ch_back_get()) != '\n' && c != EOF)
  379.             ;
  380.         if (c == '\n')
  381.             (void) ch_forw_get();
  382.         pos = ch_tell();
  383.     }
  384.     jump_loc(pos);
  385. }
  386.  
  387. /*
  388.  * Jump to a specified position in the file.
  389.  */
  390.     public void
  391. jump_loc(pos)
  392.     POSITION pos;
  393. {
  394.     register int nline;
  395.     POSITION tpos;
  396.  
  397.     /*
  398.      * See if the desired line is BEFORE the currently
  399.      * displayed screen.  If so, see if it is close enough 
  400.      * to scroll backwards to it.
  401.      * {{ This can be expensive if he has specified a very
  402.      *    large back_scroll count.  Perhaps we should put
  403.      *    some sanity limit on the loop count here. }}
  404.      */
  405.     tpos = position(TOP);
  406.     if (tpos != NULL_POSITION && pos < tpos)
  407.     {
  408.         int bs = get_back_scroll();
  409.         for (nline = 1;  nline <= bs;  nline++)
  410.         {
  411.             tpos = back_line(tpos);
  412.             if (tpos == NULL_POSITION)
  413.                 break;
  414.             if (tpos <= pos)
  415.             {
  416.                 back(nline, position(TOP), 1, 0);
  417.                 return;
  418.             }
  419.         }
  420.     } else if ((nline = onscreen(pos)) >= 0)
  421.     {
  422.         /*
  423.          * The line is currently displayed.  
  424.          * Just scroll there.
  425.          */
  426.         forw(nline, position(BOTTOM_PLUS_ONE), 1, 0);
  427.         return;
  428.     }
  429.  
  430.     /*
  431.      * Line is not on screen.
  432.      * Remember where we were; clear and paint the screen.
  433.      */
  434.     if (ch_seek(pos))
  435.     {
  436.         error("Cannot seek to that position");
  437.         return;
  438.     }
  439.     lastmark();
  440.     prepaint(pos);
  441. }
  442.  
  443. /*
  444.  * The table of marks.
  445.  * A mark is simply a position in the file.
  446.  */
  447. #define    NMARKS        (27)        /* 26 for a-z plus one for quote */
  448. #define    LASTMARK    (NMARKS-1)    /* For quote */
  449. static POSITION marks[NMARKS];
  450.  
  451. /*
  452.  * Initialize the mark table to show no marks are set.
  453.  */
  454.     public void
  455. init_mark()
  456. {
  457.     int i;
  458.  
  459.     for (i = 0;  i < NMARKS;  i++)
  460.         marks[i] = NULL_POSITION;
  461. }
  462.  
  463. /*
  464.  * See if a mark letter is valid (between a and z).
  465.  */
  466.     static int
  467. badmark(c)
  468.     int c;
  469. {
  470.     if (c < 'a' || c > 'z')
  471.     {
  472.         error("Choose a letter between 'a' and 'z'");
  473.         return (1);
  474.     }
  475.     return (0);
  476. }
  477.  
  478. /*
  479.  * Set a mark.
  480.  */
  481.     public void
  482. setmark(c)
  483.     int c;
  484. {
  485.     if (badmark(c))
  486.         return;
  487.     marks[c-'a'] = position(TOP);
  488. }
  489.  
  490.     public void
  491. lastmark()
  492. {
  493.     marks[LASTMARK] = position(TOP);
  494. }
  495.  
  496. /*
  497.  * Go to a previously set mark.
  498.  */
  499.     public void
  500. gomark(c)
  501.     int c;
  502. {
  503.     POSITION pos;
  504.  
  505.     if (c == '\'')
  506.         pos = marks[LASTMARK];
  507.     else if (badmark(c))
  508.         return;
  509.     else 
  510.         pos = marks[c-'a'];
  511.  
  512.     if (pos == NULL_POSITION)
  513.         error("mark not set");
  514.     else
  515.         jump_loc(pos);
  516. }
  517.  
  518. /*
  519.  * Get the backwards scroll limit.
  520.  * Must call this function instead of just using the value of
  521.  * back_scroll, because the default case depends on sc_height and
  522.  * top_scroll, as well as back_scroll.
  523.  */
  524.     public int
  525. get_back_scroll()
  526. {
  527.     if (back_scroll >= 0)
  528.         return (back_scroll);
  529.     if (top_scroll)
  530.         return (sc_height - 2);
  531.     return (sc_height - 1);
  532. }
  533.  
  534. /*
  535.  * Search for the n-th occurence of a specified pattern, 
  536.  * either forward (direction == '/'), or backwards (direction == '?').
  537.  */
  538.     public void
  539. search(direction, pattern, n)
  540.     int direction;
  541.     char *pattern;
  542.     register int n;
  543. {
  544.     register int search_forward = (direction == '/');
  545.     POSITION pos, linepos;
  546.  
  547. #if RECOMP
  548.     char *re_comp();
  549.     char *errmsg;
  550.  
  551.     /*
  552.      * (re_comp handles a null pattern internally, 
  553.      *  so there is no need to check for a null pattern here.)
  554.      */
  555.     if ((errmsg = re_comp(pattern)) != NULL)
  556.     {
  557.         error(errmsg);
  558.         return;
  559.     }
  560. #else
  561. #if REGCMP
  562.     char *regcmp();
  563.     static char *cpattern = NULL;
  564.  
  565.     if (pattern == NULL || *pattern == '\0')
  566.     {
  567.         /*
  568.          * A null pattern means use the previous pattern.
  569.          * The compiled previous pattern is in cpattern, so just use it.
  570.          */
  571.         if (cpattern == NULL)
  572.         {
  573.             error("No previous regular expression");
  574.             return;
  575.         }
  576.     } else
  577.     {
  578.         /*
  579.          * Otherwise compile the given pattern.
  580.          */
  581.         char *s;
  582.         if ((s = regcmp(pattern, 0)) == NULL)
  583.         {
  584.             error("Invalid pattern");
  585.             return;
  586.         }
  587.         if (cpattern != NULL)
  588.             free(cpattern);
  589.         cpattern = s;
  590.     }
  591. #else
  592.     static char lpbuf[100];
  593.     static char *last_pattern = NULL;
  594.  
  595.     if (pattern == NULL || *pattern == '\0')
  596.     {
  597.         /*
  598.          * Null pattern means use the previous pattern.
  599.          */
  600.         if (last_pattern == NULL)
  601.         {
  602.             error("No previous regular expression");
  603.             return;
  604.         }
  605.         pattern = last_pattern;
  606.     } else
  607.     {
  608.         strcpy(lpbuf, pattern);
  609.         last_pattern = lpbuf;
  610.     }
  611. #endif
  612. #endif
  613.  
  614.     /*
  615.      * Figure out where to start the search.
  616.      */
  617.  
  618.     if (position(TOP) == NULL_POSITION)
  619.     {
  620.         /*
  621.          * Nothing is currently displayed.
  622.          * Start at the beginning of the file.
  623.          * (This case is mainly for first_cmd searches,
  624.          * for example, "+/xyz" on the command line.)
  625.          */
  626.         pos = (POSITION)0;
  627.     } else if (!search_forward)
  628.     {
  629.         /*
  630.          * Backward search: start just before the top line
  631.          * displayed on the screen.
  632.          */
  633.         pos = position(TOP);
  634.     } else if (top_search)
  635.     {
  636.         /*
  637.          * Forward search and "start from top".
  638.          * Start at the second line displayed on the screen.
  639.          */
  640.         pos = position(TOP_PLUS_ONE);
  641.     } else
  642.     {
  643.         /*
  644.          * Forward search but don't "start from top".
  645.          * Start just after the bottom line displayed on the screen.
  646.          */
  647.         pos = position(BOTTOM_PLUS_ONE);
  648.     }
  649.  
  650.     if (pos == NULL_POSITION)
  651.     {
  652.         /*
  653.          * Can't find anyplace to start searching from.
  654.          */
  655.         error("Nothing to search");
  656.         return;
  657.     }
  658.  
  659.     for (;;)
  660.     {
  661.         /*
  662.          * Get lines until we find a matching one or 
  663.          * until we hit end-of-file (or beginning-of-file 
  664.          * if we're going backwards).
  665.          */
  666.         if (sigs)
  667.             /*
  668.              * A signal aborts the search.
  669.              */
  670.             return;
  671.  
  672.         if (search_forward)
  673.         {
  674.             /*
  675.              * Read the next line, and save the 
  676.              * starting position of that line in linepos.
  677.              */
  678.             linepos = pos;
  679.             pos = forw_raw_line(pos);
  680.         } else
  681.         {
  682.             /*
  683.              * Read the previous line and save the
  684.              * starting position of that line in linepos.
  685.              */
  686.             pos = back_raw_line(pos);
  687.             linepos = pos;
  688.         }
  689.  
  690.         if (pos == NULL_POSITION)
  691.         {
  692.             /*
  693.              * We hit EOF/BOF without a match.
  694.              */
  695.             error("Pattern not found");
  696.             return;
  697.         }
  698.  
  699.         /*
  700.          * Test the next line to see if we have a match.
  701.          * This is done in a variety of ways, depending
  702.          * on what pattern matching functions are available.
  703.          */
  704. #if REGCMP
  705.         if ( (regex(cpattern, line) != NULL)
  706. #else
  707. #if RECOMP
  708.         if ( (re_exec(line) == 1)
  709. #else
  710.         if ( (match(pattern, line))
  711. #endif
  712. #endif
  713.                 && (--n <= 0) )
  714.             /*
  715.              * Found the matching line.
  716.              */
  717.             break;
  718.     }
  719.  
  720.     jump_loc(linepos);
  721. }
  722.  
  723. #if (!REGCMP) && (!RECOMP)
  724. /*
  725.  * We have neither regcmp() nor re_comp().
  726.  * We use this function to do simple pattern matching.
  727.  * It supports no metacharacters like *, etc.
  728.  */
  729.     static int
  730. match(pattern, buf)
  731.     char *pattern, *buf;
  732. {
  733.     register char *pp, *lp;
  734.  
  735.     for ( ;  *buf != '\0';  buf++)
  736.     {
  737.         for (pp = pattern, lp = buf;  *pp == *lp;  pp++, lp++)
  738.             if (*pp == '\0' || *lp == '\0')
  739.                 break;
  740.         if (*pp == '\0')
  741.             return (1);
  742.     }
  743.     return (0);
  744. }
  745. #endif
  746.